package com.zmj.security.filter;

import com.zmj.security.Authenticator;
import com.zmj.security.CurrentUserHolder;
import com.zmj.security.annotation.AuthRequired;
import com.zmj.security.annotation.IgnoreAuth;
import com.zmj.security.model.UserSubject;
import org.springframework.http.HttpStatus;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: zmj
 * @Date: 2025/1/14
 */
public class AuthenticationFilter extends OncePerRequestFilter {

    private List<Authenticator> authenticators;

    public AuthenticationFilter(Authenticator ...authenticators) {
        for (Authenticator authenticator : authenticators) {
            addAuthenticator(authenticator);
        }
    }

    public void addAuthenticator(Authenticator authenticator) {
        if (authenticators == null) {
            authenticators = new ArrayList<>();
        }
        authenticators.add(authenticator);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        Object handler = request.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE);
        if (!(handler instanceof HandlerMethod)) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
        } else {
            if (authenticators == null || authenticators.isEmpty()) {
                filterChain.doFilter(request, response);
            } else {
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                if (authRequired(handlerMethod)) {
                    // 走认证逻辑
                    Authenticator authenticator = null;
                    for (Authenticator item : authenticators) {
                        if (item.support(request)) {
                            authenticator = item;
                            break;
                        }
                    }
                    if (authenticator == null) {
                        filterChain.doFilter(request, response);
                    } else {
                        UserSubject userSubject = authenticator.authenticate(request);
                        if (userSubject.isAuthenticated()) {
                            CurrentUserHolder.set(userSubject);
                            filterChain.doFilter(request, response);
                        } else {
                            response.setStatus(HttpStatus.UNAUTHORIZED.value());
                        }
                    }
                } else {
                    filterChain.doFilter(request, response);
                }
            }
        }
    }

    private boolean authRequired(HandlerMethod handlerMethod) {
        Method method = handlerMethod.getMethod();
        AuthRequired methodAuth = method.getDeclaredAnnotation(AuthRequired.class);
        IgnoreAuth methodIgnore = method.getDeclaredAnnotation(IgnoreAuth.class);
        if (methodAuth != null && methodIgnore != null) {
            return true;
        } else if (methodAuth != null) {
            return true;
        } else if (methodIgnore != null) {
            return false;
        } else {
            Class<?> methodBeanType = handlerMethod.getBeanType();
            AuthRequired classAuth = methodBeanType.getDeclaredAnnotation(AuthRequired.class);
            IgnoreAuth classIgnore = methodBeanType.getDeclaredAnnotation(IgnoreAuth.class);
            if (classAuth != null && classIgnore != null) {
                return true;
            } else if (classAuth != null) {
                return true;
            } else if (classIgnore != null) {
                return false;
            } else {
                return false;
            }
        }
    }

}
